# -*- coding:utf-8 -*-
from typing import ClassVar, Optional, List, Dict, Any, Union

from redis import Redis
from redis.commands.search.field import TextField, NumericField
from redis.commands.search.indexDefinition import IndexDefinition
from redis.commands.search.result import Result
from pydantic import BaseModel
from pydantic import ValidationError

from ..exceptions import SearchError

class SearchableModel(BaseModel):
    """RediSearch 集成混入类（必须与 OrmModel 一起使用）"""
    __search_index__: ClassVar[bool] = False
    __search_schema__: ClassVar[Dict[str, Any]] = None

    @classmethod
    def _get_redis_connection(cls) -> Redis:
        """从 OrmModel 类属性获取 Redis 连接"""
        if not hasattr(cls, "__redis_conn__") or cls.__redis_conn__ is None:
            raise ConnectionError(
                f"模型 {cls.__name__} 未配置 Redis 连接，请先调用 {cls.__name__}.set_redis_connection()"
            )
        return cls.__redis_conn__

    @classmethod
    def _check_index_exists(cls) -> bool:
    # 获取Redis连接
        redis_conn = cls._get_redis_connection()
        try:
        # 尝试获取当前类名对应的Redis索引信息
            redis_conn.ft(cls.__name__).info()
        # 如果成功获取到索引信息，返回True
            return True
        except Exception:
        # 如果在获取索引信息时发生异常，返回False
            return False

    @classmethod
    def create_search_index(cls, redis_conn: Redis):
    # 检查是否需要创建搜索索引
        if not cls.__search_index__:
            return

        # 检查必要属性
        if not hasattr(cls, "model_fields"):
        # 如果类没有 model_fields 属性，抛出 AttributeError
            raise AttributeError("SearchableModel 必须与 Pydantic 模型类共同使用")
        if not hasattr(cls, "__redis_key_prefix__"):
        # 如果类没有 __redis_key_prefix__ 属性，抛出 AttributeError
            raise AttributeError("SearchableModel 必须与 OrmModel 共同使用")

        # 生成索引字段
        fields = []
        for name, field_info in cls.model_fields.items():  # 使用 Pydantic v2 的正确属性
            field_type = field_info.annotation
        # 根据字段类型添加相应的索引字段
            if field_type == str:
                fields.append(TextField(name))
            elif field_type in (int, float):
                fields.append(NumericField(name))

        # 定义索引
        prefix = f"{cls.__redis_key_prefix__}:{cls.__name__}:"  # 正确获取前缀
        definition = IndexDefinition(
            prefix=[prefix],  # 设置索引前缀
            index_type="HASH"  # 设置索引类型为 HASH
        )
    # 在 Redis 中创建索引
        redis_conn.ft(cls.__name__).create_index(
            fields=fields,  # 设置索引字段
            definition=definition  # 设置索引定义
        )

    @classmethod
    def search(
            cls,
            query: str,
            highlight_fields: Optional[List[str]] = None,
            return_fields: Optional[List[str]] = None,
            offset: int = 0,
            limit: int = 10
    ) -> Result | None:
        """执行 RediSearch 查询"""
    # 检查类是否启用了搜索索引
        if not cls.__search_index__:
        # 如果未启用搜索索引，抛出 SearchError 异常
            raise SearchError(f"模型 {cls.__name__} 未启用搜索索引")

    # 获取 Redis 连接
        redis_conn = cls._get_redis_connection()

        # 构建完整查询，包括查询条件和分页限制
        full_query = f"{query} LIMIT {offset} {limit}"

        # 如果指定了返回字段，添加 RETURN 子句
        if return_fields:
            # 将返回字段列表转换为字符串
            fields_str = " ".join(return_fields)
            # 添加 RETURN 子句到查询中
            if return_fields:
                fields_str = " ".join(return_fields)
                full_query += f" RETURN {len(return_fields)} {fields_str}"

            # 添加 HIGHLIGHT
            if highlight_fields:
                fields_str = " ".join(highlight_fields)
                full_query += f" HIGHLIGHT FIELDS {len(highlight_fields)} {fields_str}"

            # 执行查询
            return redis_conn.ft(cls.__name__).search(full_query)

    @classmethod
    def search_by_keyword(
            cls,
            keyword: str,
            field: str = "name",
            fuzzy: bool = True
    ) -> Result:
        """关键词搜索（支持模糊匹配）"""
        query = f"@{field}:{'%%%s%%' % keyword if fuzzy else '%s' % keyword}"
        return cls.search(query)

    @classmethod
    def search_and_parse(cls, query: str, **kwargs) -> List["SearchableModel"]:
        """执行搜索并解析为模型实例"""
        result = cls.search(query, **kwargs)
        instances = []

        for doc in result.docs:
            try:
                # 提取模型定义的字段名（含别名）
                model_fields = cls.model_fields.keys()
                data = {
                    # 处理字段别名：将 Redis 字段名映射到模型字段名
                    field_name: cls._parse_value(field_name, doc.__getattr__(redis_field))
                    for redis_field, field_name in cls._get_field_mapping().items()
                    if hasattr(doc, redis_field)
                }
                instances.append(cls(**data))
            except ValidationError as e:
                raise ValidationError(f"解析文档 {doc.id} 失败: {e.errors()}")
            except KeyError as e:
                raise KeyError(f"字段映射错误: {str(e)}")
        return instances

    @classmethod
    def _get_field_mapping(cls) -> Dict[str, str]:
        """获取 Redis 字段名到模型字段名的映射（处理别名）"""
        return {
            field_info.alias or field_name: field_name
            for field_name, field_info in cls.model_fields.items()
        }

    @classmethod
    def _parse_value(cls, field: str, value: Any) -> Any:
        """根据模型字段类型转换值"""
        field_info = cls.model_fields[field]
        field_type = field_info.annotation

        try:
            # 处理 Optional 类型
            if hasattr(field_type, "__origin__") and field_type.__origin__ == Union:
                field_type = field_type.__args__[0]

            # 类型转换逻辑
            if field_type == int:
                return int(value)
            elif field_type == float:
                return float(value)
            elif field_type == bool:
                return str(value).lower() in ["true", "1", "yes"]
            else:
                return value
        except Exception as e:
            print(f"字段 {field} 值 '{value}' 转换失败: {str(e)}")
            return value  # 保持原始值由 Pydantic 验证
